package org.jsola.hr.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.jsola.core.Page;
import org.jsola.hr.dao.ICompanyDAO;
import org.jsola.hr.dao.IEmpSocInfoDAO;
import org.jsola.hr.dao.ISocProgItemDAO;
import org.jsola.hr.entity.CompanyDO;
import org.jsola.hr.entity.SocProgDO;
import org.jsola.hr.entity.SocProgItemDO;
import org.jsola.hr.exception.HrException;
import org.jsola.hr.query.SocStatisticalQuery;
import org.jsola.hr.service.IEmpSocInfoService;
import org.jsola.hr.service.IEmpSocMonthService;
import org.jsola.hr.service.ISocProgService;
import org.jsola.hr.service.ISocStatisticalService;
import org.jsola.hr.vo.EmpSocInfoListVO;
import org.jsola.hr.vo.EmpSocInfoStatisticalExportVO;
import org.jsola.hr.vo.SocAmountVO;
import org.jsola.hr.vo.SocCityAndProgStatisticalVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static org.jsola.hr.constant.EmpSocStatus.ATTRITION;
import static org.jsola.hr.constant.EmpSocStatus.INCREMENT;
import static org.jsola.hr.constant.HrConstants.*;

/**
 * @author lyh
 *
 * 社保统计
 */

@Service("socStatisticalServiceImpl")
public class SocStatisticalServiceImpl implements ISocStatisticalService {

    @Autowired
    private IEmpSocInfoDAO empSocInfoDAO;

    @Autowired
    private ISocProgItemDAO socProgItemDAO;

    @Autowired
    private ISocProgService socProgService;

    @Autowired
    private ICompanyDAO companyDAO;

    @Autowired
    private IEmpSocMonthService empSocMonthService;

    @Autowired
    private IEmpSocInfoService empSocInfoService;

    private static final List<String> itemType = new ArrayList<String>(){
        {
            add("养老保险");
            add("医疗保险");
            add("失业保险");
            add("工伤保险");
            add("生育保险");
            add("补充大病医保");
            add("补充养老保险");
            add("残保金");
            add("其他");
        }
    };

    @Override
    public SocAmountVO selectAmount(SocStatisticalQuery socStatisticalQuery, String siteId) {
        List<String> monthIdList = empSocMonthService.selectMonthIdForWhile(socStatisticalQuery.getStartYear(), socStatisticalQuery.getStartMonth(),
                socStatisticalQuery.getEndYear(), socStatisticalQuery.getEndMonth(), socStatisticalQuery.getCompanyId(),
                socStatisticalQuery.getIsAcrossYear(), siteId);
        if(CollectionUtils.isEmpty(monthIdList)){
            SocAmountVO result =  new SocAmountVO();
            result.setCompanyPfAmount(new BigDecimal("0"));
            result.setCompanySocAmount(new BigDecimal("0"));
            result.setEmpSocInfoCount(0);
            result.setPersonalPfAmount(new BigDecimal("0"));
            result.setPersonalSocAmount(new BigDecimal("0"));
            result.setSocAmount(new BigDecimal("0"));
            result.setPfAmount(new BigDecimal("0"));
            return result;
        }
        socStatisticalQuery.setMonthIdList(monthIdList);
        List<SocAmountVO> socAmountVOList = empSocInfoDAO.selectAmount(socStatisticalQuery, siteId);
        if(CollectionUtils.isEmpty(socAmountVOList)){
            return new SocAmountVO();
        }
        //如果年份跨度大于等于1
        if(socStatisticalQuery.getStep() >= 1){
            for(int stepYear = 1; stepYear <= socStatisticalQuery.getStep(); stepYear ++){
                int nextYear = Integer.parseInt(socStatisticalQuery.getStartYear());
                nextYear += stepYear;
                List<String> oneYearMonthIdList =
                        empSocMonthService.selectOneYearMonthId(Integer.toString(nextYear), socStatisticalQuery.getCompanyId(), siteId);
                SocAmountVO socAmountVO = empSocInfoDAO.selectOneYearAmount(oneYearMonthIdList
                        , socStatisticalQuery.getCompanyId()
                        , siteId);
                socAmountVOList.add(socAmountVO);
            }
        }
        //返回值定义
        SocAmountVO socAmountVO = new SocAmountVO();
        for (int i = 0; i < socAmountVOList.size(); i++) {
            //第一次是赋值而不是相加
            if(i == 0){
                //公司社保总计第一次赋值
                socAmountVO.setCompanySocAmount(socAmountVOList.get(i).getCompanySocAmount());
                //公司公积金总计第一次赋值
                socAmountVO.setCompanyPfAmount(socAmountVOList.get(i).getCompanyPfAmount());
                //个人社保总计第一次赋值
                socAmountVO.setPersonalSocAmount(socAmountVOList.get(i).getPersonalSocAmount());
                //个人公积金总计第一次赋值
                socAmountVO.setPersonalPfAmount(socAmountVOList.get(i).getPersonalPfAmount());
                //参保人次第一次赋值
                socAmountVO.setEmpSocInfoCount(socAmountVOList.get(i).getEmpSocInfoCount());
            } else {
                //公司社保总计累加
                socAmountVO.setCompanySocAmount(socAmountVO.getCompanySocAmount().add(socAmountVOList.get(i).getCompanySocAmount()));
                //公司公积金总计累加
                socAmountVO.setCompanyPfAmount(socAmountVO.getCompanyPfAmount().add(socAmountVOList.get(i).getCompanyPfAmount()));
                //个人社保总计累加
                socAmountVO.setPersonalSocAmount(socAmountVO.getPersonalSocAmount().add(socAmountVOList.get(i).getPersonalSocAmount()));
                //个人公积金总计累加
                socAmountVO.setPersonalPfAmount(socAmountVO.getPersonalPfAmount().add(socAmountVOList.get(i).getPersonalPfAmount()));
                //参保人次累加
                socAmountVO.setEmpSocInfoCount(socAmountVO.getEmpSocInfoCount() + socAmountVOList.get(i).getEmpSocInfoCount());
            }
        }
        //社保总计
        socAmountVO.setSocAmount(socAmountVO.getCompanySocAmount().add(socAmountVO.getPersonalSocAmount()));
        //公积金总计
        socAmountVO.setPfAmount(socAmountVO.getCompanyPfAmount().add(socAmountVO.getPersonalPfAmount()));
        Map<String, Long> integerLongMap = empSocInfoService.selectIncreaseDecreaseCount(socStatisticalQuery.getStartYear(), socStatisticalQuery.getStartMonth(),
                socStatisticalQuery.getEndYear(), socStatisticalQuery.getEndMonth(), socStatisticalQuery.getCompanyId(),
                socStatisticalQuery.getIsAcrossYear(), siteId);
        integerLongMap.putIfAbsent(INCREASE_STR, 0L);
        integerLongMap.putIfAbsent(DECREASE_STR, 0L);
        socAmountVO.setIncrement(integerLongMap.get(INCREASE_STR));
        socAmountVO.setRevoke(integerLongMap.get(DECREASE_STR));
        //如果开始年份,结束年份,开始月份,结束月份都相同。代表只需要查一个月的总额,此时需要计算与上月的差值
        if(socStatisticalQuery.getStartYear().equals(socStatisticalQuery.getEndYear()) &&
                socStatisticalQuery.getStartMonth().equals(socStatisticalQuery.getEndMonth())){
            calculationAmountDifference(socStatisticalQuery,socAmountVO,siteId);
        }
        //如果没有传公司ID说明是租户级查询,需要赋值用工单位数量
        if(socStatisticalQuery.getCompanyId() == null){
            Example example = new Example(CompanyDO.class);
            example.and()
                    .andEqualTo("valid", true)
                    .andEqualTo("siteId", siteId);
            socAmountVO.setCompanyCount(companyDAO.selectCountByExample(example));
        }

        return socAmountVO;
    }

    @Override
    public List<SocAmountVO> selectMonthStatistical(SocStatisticalQuery socStatisticalQuery, String siteId){
        List<String> monthIdList = empSocMonthService.selectMonthIdForWhile(socStatisticalQuery.getStartYear(), socStatisticalQuery.getStartMonth(),
                socStatisticalQuery.getEndYear(), socStatisticalQuery.getEndMonth(), socStatisticalQuery.getCompanyId(),
                socStatisticalQuery.getIsAcrossYear(), siteId);
        if(CollectionUtils.isEmpty(monthIdList)){
            return new ArrayList<>();
        }
        socStatisticalQuery.setMonthIdList(monthIdList);
        List<SocAmountVO> socAmountVOList = empSocInfoDAO.selectMonthStatistical(socStatisticalQuery, siteId);
        if(CollectionUtils.isEmpty(socAmountVOList)){
            return new ArrayList<>();
        }
        for (SocAmountVO socAmountVO : socAmountVOList) {
            socAmountVO.setSocAmount(socAmountVO.getCompanySocAmount().add(socAmountVO.getPersonalSocAmount()));
            socAmountVO.setPfAmount(socAmountVO.getCompanyPfAmount().add(socAmountVO.getPersonalPfAmount()));
        }
        return socAmountVOList;
    }

    @Override
    public List<SocAmountVO> selectCompanyStatistical(SocStatisticalQuery socStatisticalQuery, String siteId){
        List<String> monthIdList = empSocMonthService.selectMonthIdForWhile(socStatisticalQuery.getStartYear(), socStatisticalQuery.getStartMonth(),
                socStatisticalQuery.getEndYear(), socStatisticalQuery.getEndMonth(), socStatisticalQuery.getCompanyId(),
                socStatisticalQuery.getIsAcrossYear(), siteId);
        if(CollectionUtils.isEmpty(monthIdList)){
            return new ArrayList<>();
        }
        socStatisticalQuery.setMonthIdList(monthIdList);
        List<SocAmountVO> socAmountVOList = empSocInfoDAO.selectCompanyStatistical(socStatisticalQuery, siteId);
        //sql中只以 用工单位或分公司 做groupBy 但是跨年时做union all 用工单位或分公司 可能重复所以需要再吧重复的数据金额相加
        //只有跨年时才有可能 用工单位或分公司 重复,所以只做跨年时的判断
        if(socStatisticalQuery.getIsAcrossYear()){
            List<SocAmountVO> returnSocAmountVOList = new ArrayList<>();
            Map<String, List<SocAmountVO>> nameListMap;
            //默认以用工单位名称分组
            if(!socStatisticalQuery.getGroupChild()){
                nameListMap = socAmountVOList
                        .stream()
                        .collect(Collectors.groupingBy(SocAmountVO::getCompanyName));
            } else {
                nameListMap = socAmountVOList
                        .stream()
                        .collect(Collectors.groupingBy(SocAmountVO::getChildCompanyName));
            }
            //遍历map的所有key,这里不遍历所有value是因为key下面还有用
            for (String companyName : nameListMap.keySet()) {
                //在 Map 中拿到这个分组
                List<SocAmountVO> keySocAmountVOList = nameListMap.get(companyName);
                //如果这个分组数量大于1,则需要吧list中的金额全部相加
                SocAmountVO socAmountVO;
                if(keySocAmountVOList.size() > 1){
                    socAmountVO = new SocAmountVO();
                    socAmountVO.setCompanyName(companyName);
                    //先把四个金额赋0,否则下面for循环会出现空指针 分别是:个人社保,个人公积金,公司社保,公司公积金
                    socAmountVO.setPersonalSocAmount(new BigDecimal("0"));
                    socAmountVO.setPersonalPfAmount(new BigDecimal("0"));
                    socAmountVO.setCompanySocAmount(new BigDecimal("0"));
                    socAmountVO.setCompanyPfAmount(new BigDecimal("0"));
                    //循环相加金额
                    keySocAmountVOList.forEach(amountVO->{
                        socAmountVO.setPersonalSocAmount(socAmountVO.getPersonalSocAmount().add(amountVO.getPersonalSocAmount()));
                        socAmountVO.setPersonalPfAmount(socAmountVO.getPersonalPfAmount().add(amountVO.getPersonalPfAmount()));
                        socAmountVO.setCompanySocAmount(socAmountVO.getCompanySocAmount().add(amountVO.getCompanySocAmount()));
                        socAmountVO.setCompanyPfAmount(socAmountVO.getCompanyPfAmount().add(amountVO.getCompanyPfAmount()));
                    });
                } else {
                    //else说明list中只有一个元素
                    socAmountVO = keySocAmountVOList.get(0);
                }
                //计算社保和公积金的总金额
                socAmountVO.setPfAmount(socAmountVO.getPersonalPfAmount().add(socAmountVO.getCompanyPfAmount()));
                socAmountVO.setSocAmount(socAmountVO.getPersonalSocAmount().add(socAmountVO.getCompanySocAmount()));
                returnSocAmountVOList.add(socAmountVO);
            }
            return returnSocAmountVOList;
        }
        socAmountVOList.forEach(socAmountVO->{
            socAmountVO.setPfAmount(socAmountVO.getPersonalPfAmount().add(socAmountVO.getCompanyPfAmount()));
            socAmountVO.setSocAmount(socAmountVO.getPersonalSocAmount().add(socAmountVO.getCompanySocAmount()));
        });
        return socAmountVOList;
    }

    @Override
    public List<SocAmountVO> selectChildCompanyStatistical(SocStatisticalQuery socStatisticalQuery, String siteId) {
        socStatisticalQuery.setGroupChild(true);
        return selectCompanyStatistical(socStatisticalQuery, siteId);
    }

    @Override
    public Page<EmpSocInfoListVO> selectEmpSocInfoStatistical(SocStatisticalQuery socStatisticalQuery, String siteId){
        List<String> monthIdList = empSocMonthService.selectMonthIdForWhile(socStatisticalQuery.getStartYear(), socStatisticalQuery.getStartMonth(),
                socStatisticalQuery.getEndYear(), socStatisticalQuery.getEndMonth(), socStatisticalQuery.getCompanyId(),
                socStatisticalQuery.getIsAcrossYear(), siteId);
        if(CollectionUtils.isEmpty(monthIdList)){
            return new Page<>(socStatisticalQuery.getPageSize(), socStatisticalQuery.getPageNo());
        }
        socStatisticalQuery.setMonthIdList(monthIdList);
        PageInfo <EmpSocInfoListVO> pageInfo = PageHelper.startPage(socStatisticalQuery.getPageNo(), socStatisticalQuery.getPageSize())
                .doSelectPageInfo(() -> empSocInfoDAO.selectEmpSocInfoStatistical(socStatisticalQuery, siteId));
        return new Page<>(pageInfo.getList(),pageInfo.getPageNum(),pageInfo.getPageSize(),(int)pageInfo.getTotal());
    }

    @Override
    public SocAmountVO selectCountAndProportion(SocStatisticalQuery socStatisticalQuery, String siteId){
        SocAmountVO socAmountVO = new SocAmountVO();
        List<String> monthIdList = empSocMonthService.selectMonthIdForWhile(socStatisticalQuery.getStartYear(), socStatisticalQuery.getStartMonth(),
                socStatisticalQuery.getEndYear(), socStatisticalQuery.getEndMonth(), socStatisticalQuery.getCompanyId(),
                socStatisticalQuery.getIsAcrossYear(), siteId);
        if(CollectionUtils.isEmpty(monthIdList)){
            return new SocAmountVO();
        }
        socStatisticalQuery.setMonthIdList(monthIdList);
        List<EmpSocInfoListVO> empSocInfoListVOList = empSocInfoDAO.selectEmpSocInfoStatistical(socStatisticalQuery, siteId);
        if(CollectionUtils.isEmpty(empSocInfoListVOList)){
            return new SocAmountVO();
        }
        for (EmpSocInfoListVO empSocInfoListVO : empSocInfoListVOList) {
            String socProgId = empSocInfoListVO.getProgId();
            if("".equals(socProgId)){
                empSocInfoListVO.setProgName("减员状态(无方案)");
                empSocInfoListVO.setSocCityName("减员状态(无参保城市)");
            } else if(socProgId.contains("importSoc") ||
                    socProgId.contains("importMe") ||
                    socProgId.contains("importPf") ||
                    socProgId.contains("batchImport") ||
                    socProgId.contains("batchImportPf")) {
                empSocInfoListVO.setProgName("导入数据(无方案)");
                empSocInfoListVO.setSocCityName("导入数据(无参保城市)");
            } else if(SYNC_DATE.equals(socProgId)){
                empSocInfoListVO.setProgName("同步数据");
                empSocInfoListVO.setSocCityName("同步数据(无参保城市)");
            }
        }
        //按照参保方案分组并计算每组数量
        Map<String, Long> socProgNameStatistical = empSocInfoListVOList.stream()
                .filter(empSocInfoListVO -> empSocInfoListVO.getProgName()!=null)
                .collect(Collectors.groupingBy(EmpSocInfoListVO::getProgName, Collectors.counting()));
        //赋值参保方案数量
        socAmountVO.setSocProgCount(socProgNameStatistical.size());
        //按照参保地分组并计算每组数量
        Map<String, Long> socCityNameStatistical = empSocInfoListVOList.stream().
                collect(Collectors.groupingBy(EmpSocInfoListVO::getSocCityName, Collectors.counting()));
        //赋值参保地数量
        socAmountVO.setSocCityCount(socCityNameStatistical.size());

        List<SocCityAndProgStatisticalVO> socProgStatistical = new ArrayList<>();
        List<SocCityAndProgStatisticalVO> socCityStatistical = new ArrayList<>();
        //填充参保方案List
        fillCountAndProportion(socProgNameStatistical,socProgStatistical,empSocInfoListVOList.size(),"socProg");
        //填充参保地List
        fillCountAndProportion(socCityNameStatistical,socCityStatistical,empSocInfoListVOList.size(),"socCity");
        socAmountVO.setSocProgStatistical(socProgStatistical);
        socAmountVO.setSocCityStatistical(socCityStatistical);
        return socAmountVO;

    }

    @Override
    public SocAmountVO selectSocProgItem(SocStatisticalQuery socStatisticalQuery, String siteId) {
        SocAmountVO socAmountVO = new SocAmountVO();
        List<String> monthIdList = empSocMonthService.selectMonthIdForWhile(socStatisticalQuery.getStartYear(), socStatisticalQuery.getStartMonth(),
                socStatisticalQuery.getEndYear(), socStatisticalQuery.getEndMonth(), socStatisticalQuery.getCompanyId(),
                socStatisticalQuery.getIsAcrossYear(), siteId);
        if(CollectionUtils.isEmpty(monthIdList)){
            return new SocAmountVO();
        }
        socStatisticalQuery.setMonthIdList(monthIdList);
        //查询到某个月或者某个阶段的所有人员
        List<EmpSocInfoListVO> empSocInfoListVOList = empSocInfoDAO.selectEmpSocInfoStatistical(socStatisticalQuery, siteId);
        if(CollectionUtils.isEmpty(empSocInfoListVOList)){
            return new SocAmountVO();
        }
        //按照参保方案分组
        Map<String, List<EmpSocInfoListVO>> socProgGroupMap = empSocInfoListVOList.stream().
                collect(Collectors.groupingBy(EmpSocInfoListVO::getProgId));
        //返回值核心参数定义
        Map<String, BigDecimal> socDetail = new HashMap<>(32);
        //先把其他项赋值
        socDetail.put("其他",new BigDecimal("0"));
        //遍历参保方案组Id
        for (String socProgId : socProgGroupMap.keySet()) {
            //查询到这个参保方案
            SocProgDO socProgDO = socProgService.selectDOById(socProgId, siteId);
            if(ObjectUtils.isEmpty(socProgDO)){
                continue;
            }
            //查询这个参保方案的社保的所有子项
            Example socProgItemExample = new Example(SocProgItemDO.class);
            socProgItemExample.and()
                    .andEqualTo("valid", true)
                    .andEqualTo("siteId", siteId)
                    .andEqualTo("itemType", 1)
                    .andEqualTo("progId",socProgId);
            List<SocProgItemDO> socProgItemDOList = socProgItemDAO.selectByExample(socProgItemExample);
            //如果仅设置参保金额
            if(socProgDO.getOnlySocAmount()){
                calculationAmountByFixed(socProgItemDOList,socStatisticalQuery.getQueryType(),socProgId,socProgGroupMap,socDetail);
            } else {
                //按照比例设置参保金额
                calculationAmountByProportion(socProgItemDOList,socStatisticalQuery.getQueryType(),socProgId,socProgGroupMap,socDetail);
            }
        }
        socAmountVO.setSocDetail(socDetail);
        //如果开始年份,结束年份,开始月份,结束月份都相同。代表只需要查一个月的总额,此时需要计算与上月的差值
        if(socStatisticalQuery.getStartYear().equals(socStatisticalQuery.getEndYear()) &&
                socStatisticalQuery.getStartMonth().equals(socStatisticalQuery.getEndMonth())){
            calculationItemDifference(socStatisticalQuery,socAmountVO,siteId);
        }
        return socAmountVO;
    }

    /**
     * 按照比例计算金额
     * 被 selectSocProgItem 方法调用
     * @param socProgItemDOList 参保方案子项List
     * @param queryType         查询类型
     * @param socProgId         参保方案Id
     * @param socProgGroupMap   按照参保方案分组后的Map
     * @param socDetail         核心参数
     */
    private void calculationAmountByProportion(List<SocProgItemDO> socProgItemDOList,
                                         Integer queryType,
                                         String socProgId,
                                         Map<String, List<EmpSocInfoListVO>> socProgGroupMap,
                                         Map<String, BigDecimal> socDetail){
        //如果按照比例设置参保
        //循环这个参保方案的所有子项
        for (SocProgItemDO socProgItemDO : socProgItemDOList) {
            BigDecimal oneHundredPercent = new BigDecimal("0.01");
            if(queryType == 1){
                //个人公司总计
                //公司缴纳比例*0.01
                BigDecimal companyProportion = socProgItemDO.getPersentCompany().multiply(oneHundredPercent);
                //个人缴纳比例*0.01
                BigDecimal personalProportion = socProgItemDO.getPersentPersonal().multiply(oneHundredPercent);
                //这个参保方案的这个子项的公司缴纳金额
                BigDecimal companyAmount = socProgItemDO.getCardinalityDefault().multiply(companyProportion);
                //这个参保方案的这个子项的个人缴纳金额
                BigDecimal personalAmount = socProgItemDO.getCardinalityDefault().multiply(personalProportion);
                //个人金额加公司金额
                BigDecimal amountTotal = companyAmount.add(personalAmount);
                //这个方案的这个子项的参保人数
                BigDecimal socProgEmpCount = new BigDecimal(socProgGroupMap.get(socProgId).size());
                //这个参保方案的这个子项个人加公司的金额总数
                BigDecimal socProgTotal = amountTotal.multiply(socProgEmpCount);
                fillMap(socDetail,socProgItemDO,socProgTotal);
            } else if(queryType == 2){
                //公司总计
                //公司缴纳比例*0.01
                BigDecimal companyProportion = socProgItemDO.getPersentCompany().multiply(oneHundredPercent);
                //这个参保方案的这个子项的公司缴纳金额
                BigDecimal companyAmount = socProgItemDO.getCardinalityDefault().multiply(companyProportion);
                //这个方案的这个子项的参保人数
                BigDecimal socProgEmpCount = new BigDecimal(socProgGroupMap.get(socProgId).size());
                //这个参保方案的这个子项的公司金额总数
                BigDecimal socProgCompanyTotal = companyAmount.multiply(socProgEmpCount);
                fillMap(socDetail,socProgItemDO,socProgCompanyTotal);
            } else if(queryType == 3){
                //个人总计
                //个人缴纳比例*0.01
                BigDecimal personalProportion = socProgItemDO.getPersentPersonal().multiply(oneHundredPercent);
                //这个参保方案的这个子项的个人缴纳金额
                BigDecimal personalAmount = socProgItemDO.getCardinalityDefault().multiply(personalProportion);
                //这个方案的这个子项的参保人数
                BigDecimal socProgEmpCount = new BigDecimal(socProgGroupMap.get(socProgId).size());
                //这个参保方案的这个子项的个人金额总数
                BigDecimal socProgPersonalTotal = personalAmount.multiply(socProgEmpCount);
                fillMap(socDetail,socProgItemDO,socProgPersonalTotal);
            } else{
                throw new HrException("查询类型有误");
            }
        }
    }

    /**
     * 按照固定值计算金额
     * 被 selectSocProgItem 方法调用
     * @param socProgItemDOList 参保方案子项List
     * @param queryType         查询类型
     * @param socProgId         参保方案Id
     * @param socProgGroupMap   按照参保方案分组后的Map
     * @param socDetail         核心参数
     */
    private void calculationAmountByFixed(List<SocProgItemDO> socProgItemDOList,
                                          Integer queryType,
                                          String socProgId,
                                          Map<String, List<EmpSocInfoListVO>> socProgGroupMap,
                                          Map<String, BigDecimal> socDetail){
        //循环此参保方案子项
        for (SocProgItemDO socProgItemDO : socProgItemDOList) {
            if(queryType == 1){
                //个人公司总计
                //拿到这个参保方案的子项的公司金额
                BigDecimal amountCompany = socProgItemDO.getAmountCompany();
                //拿到这个参保方案的子项的个人金额
                BigDecimal amountPersonal = socProgItemDO.getAmountPersonal();
                //公司金额个人金额相加
                BigDecimal amountTotal = amountCompany.add(amountPersonal);
                //这个方案的这个子项的参保人数
                BigDecimal socProgEmpCount = new BigDecimal(socProgGroupMap.get(socProgId).size());
                //金额总数
                BigDecimal socProgTotal = amountTotal.multiply(socProgEmpCount);
                fillMap(socDetail,socProgItemDO,socProgTotal);
            } else if(queryType == 2){
                //公司总计
                //拿到这个参保方案的子项的公司金额
                BigDecimal amountCompany = socProgItemDO.getAmountCompany();
                //这个方案的这个子项的参保人数
                BigDecimal socProgEmpCount = new BigDecimal(socProgGroupMap.get(socProgId).size());
                //这个参保方案的这个子项的个人金额总数
                BigDecimal socProgCompanyTotal = amountCompany.multiply(socProgEmpCount);
                fillMap(socDetail,socProgItemDO,socProgCompanyTotal);
            } else if(queryType == 3){
                //个人总计
                //拿到这个参保方案的子项的个人金额
                BigDecimal amountPersonal = socProgItemDO.getAmountPersonal();
                //这个方案的这个子项的参保人数
                BigDecimal socProgEmpCount = new BigDecimal(socProgGroupMap.get(socProgId).size());
                //这个参保方案的这个子项的个人金额总数
                BigDecimal socProgPersonalTotal = amountPersonal.multiply(socProgEmpCount);
                fillMap(socDetail,socProgItemDO,socProgPersonalTotal);
            } else {
                throw new HrException("查询类型有误");
            }
        }
    }

    /**
     * 计算顶部金额总计与上个月的差值
     * 被 selectAmount 方法调用
     * @param socStatisticalQuery 查询参数
     * @param socAmountVO         返回值传参准备赋值差值
     */
    private void calculationAmountDifference(SocStatisticalQuery socStatisticalQuery, SocAmountVO socAmountVO, String siteId){
        SocStatisticalQuery previousYearAndMonth = getPreviousYearAndMonth(socStatisticalQuery);
        SocAmountVO socAmountVOPrevious = selectAmount(previousYearAndMonth, siteId);
        //较上月公司公积金
        socAmountVO.setBeforeCompanyPfAmount(socAmountVO.getCompanyPfAmount().subtract(socAmountVOPrevious.getCompanyPfAmount()));
        //较上月公司社保
        socAmountVO.setBeforeCompanySocAmount(socAmountVO.getCompanySocAmount().subtract(socAmountVOPrevious.getCompanySocAmount()));
        //较上月参保人数
        socAmountVO.setBeforeEmpSocInfoCount(socAmountVO.getEmpSocInfoCount() - socAmountVOPrevious.getEmpSocInfoCount());
        //较上月个人公积金
        socAmountVO.setBeforePersonalPfAmount(socAmountVO.getPersonalPfAmount().subtract(socAmountVOPrevious.getPersonalPfAmount()));
        //较上月个人社保
        socAmountVO.setBeforePersonalSocAmount(socAmountVO.getPersonalSocAmount().subtract(socAmountVOPrevious.getPersonalSocAmount()));
        //较上月社保总计
        socAmountVO.setBeforeSocAmount(socAmountVO.getSocAmount().subtract(socAmountVOPrevious.getSocAmount()));
        //较上月公积金总计
        socAmountVO.setBeforePfAmount(socAmountVO.getPfAmount().subtract(socAmountVOPrevious.getPfAmount()));
        Map<String, Long> integerLongMap = empSocInfoService.selectIncreaseDecreaseCount(socStatisticalQuery.getStartYear(),
                socStatisticalQuery.getStartMonth(), socStatisticalQuery.getEndYear(), socStatisticalQuery.getEndMonth(),
                socStatisticalQuery.getCompanyId(), socStatisticalQuery.getIsAcrossYear(), siteId);
        integerLongMap.putIfAbsent(INCREASE_STR, 0L);
        integerLongMap.putIfAbsent(DECREASE_STR, 0L);
        socAmountVO.setBeforeIncrement(socAmountVO.getIncrement() - integerLongMap.get(INCREASE_STR));
        socAmountVO.setBeforeRevoke(socAmountVO.getRevoke() - integerLongMap.get(DECREASE_STR));
    }

    /**
     * 计算中部社保各项详情与上个月的差值
     * 被 selectSocProgItem 方法调用
     * @param socStatisticalQuery 查询参数
     * @param socAmountVO         返回值传参准备赋值差值
     */
    private void calculationItemDifference(SocStatisticalQuery socStatisticalQuery, SocAmountVO socAmountVO, String siteId){
        Map<String,BigDecimal> socDetailDifference = new HashMap<>(32);
        SocStatisticalQuery previousYearAndMonth = getPreviousYearAndMonth(socStatisticalQuery);
        //获取到上月的数据
        SocAmountVO socAmountVOPrevious = selectSocProgItem(previousYearAndMonth, siteId);
        //本月
        Map<String, BigDecimal> socDetailCurrent = socAmountVO.getSocDetail();
        //上月
        Map<String, BigDecimal> socDetailPrevious = socAmountVOPrevious.getSocDetail();
        for (String itemName : itemType) {
            //如果,此月有此项,上月无此项,说明此月高,差值为上升
            if(socDetailCurrent.get(itemName) != null && (socDetailPrevious == null || socDetailPrevious.get(itemName) == null)){
                socDetailDifference.put(itemName,socDetailCurrent.get(itemName));
            } else if(socDetailCurrent.get(itemName) == null && (socDetailPrevious != null && socDetailPrevious.get(itemName) != null)) {
                //如果,此月无此项,上月有此项,说明上月高,差值为下降
                socDetailDifference.put(itemName,socDetailPrevious.get(itemName).multiply(new BigDecimal("-1")));
            } else if(socDetailCurrent.get(itemName) == null && (socDetailPrevious == null || socDetailPrevious.get(itemName) == null)) {
                //如果,此月上月都无此项,说明差值为0
                socDetailDifference.put(itemName,new BigDecimal("0"));
            } else {
                //如果,此月上月都有此项,需要用本月减上月的此项金额
                if(socDetailPrevious != null){
                    socDetailDifference.put(itemName,socDetailCurrent.get(itemName).subtract(socDetailPrevious.get(itemName)));
                }
            }
        }
        socAmountVO.setSocDetailDifference(socDetailDifference);
    }

    /**
     * 计算此月的上一个月
     * @param socStatisticalQuery 查询参数
     * @return 查询参数
     */
    private SocStatisticalQuery getPreviousYearAndMonth(SocStatisticalQuery socStatisticalQuery){
        //当前年份月份
        String year = socStatisticalQuery.getStartYear();
        String month = socStatisticalQuery.getStartMonth();
        LocalDate dateTime = LocalDate.parse(year + "-" + month + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        //前一个月份年份
        String yearPrevious = dateTime.minusMonths(1).getYear() + "";
        String monthPrevious = dateTime.minusMonths(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")).substring(5, 7);
        socStatisticalQuery.setStartYear(yearPrevious);
        socStatisticalQuery.setStartMonth(monthPrevious);
        socStatisticalQuery.setEndYear(null);
        socStatisticalQuery.setEndMonth(monthPrevious);
        return socStatisticalQuery;
    }

    /**
     * 填充数量和比例
     * 被 selectCountAndProportion 方法调用
     * @param map   名称和数量的对应
     * @param list  准备填充的list
     * @param count 查询到的总人数
     * @param type  需要赋值的类型
     */
    private void fillCountAndProportion(Map<String, Long> map,
                                        List<SocCityAndProgStatisticalVO> list,
                                        Integer count,
                                        String type){
        for (String name : map.keySet()) {
            SocCityAndProgStatisticalVO soc = new SocCityAndProgStatisticalVO();
            if("socCity".equals(type)){
                soc.setSocCityName(name);
                soc.setSocCityCount(map.get(name));
                soc.setSocCityProportion((float)map.get(name)/count);
                list.add(soc);
            } else if("socProg".equals(type)){
                soc.setSocProgName(name);
                soc.setSocProgCount(map.get(name));
                soc.setSocProgProportion((float)map.get(name)/count);
                list.add(soc);
            }
        }
    }

    private void fillMap(Map<String, BigDecimal> socDetail, SocProgItemDO socProgItemDO, BigDecimal socProgTotal){
        String itemName = socProgItemDO.getItemName();
        if(!itemType.contains(itemName)){
            socDetail.put("其他",socDetail.get("其他").add(socProgTotal));
            return;
        }
        BigDecimal bigDecimal = socDetail.get(itemName);
        //如果核心返回值中还未有此类子项,则添加
        if(bigDecimal == null){
            socDetail.put(socProgItemDO.getItemName(),socProgTotal);
        } else {
            //如果核心返回值中还已有此类子项,则相加
            BigDecimal addSocProgCompanyTotal = bigDecimal.add(socProgTotal);
            socDetail.put(socProgItemDO.getItemName(),addSocProgCompanyTotal);
        }
    }

    @Override
    public List<EmpSocInfoStatisticalExportVO> empSocInfoStatisticalExport(SocStatisticalQuery socStatisticalQuery, String siteId){
        List<String> monthIdList = empSocMonthService.selectMonthIdForWhile(socStatisticalQuery.getStartYear(), socStatisticalQuery.getStartMonth(),
                socStatisticalQuery.getEndYear(), socStatisticalQuery.getEndMonth(), socStatisticalQuery.getCompanyId(),
                socStatisticalQuery.getIsAcrossYear(), siteId);
        if(CollectionUtils.isEmpty(monthIdList)){
            return new ArrayList<>();
        }
        socStatisticalQuery.setMonthIdList(monthIdList);
        List<EmpSocInfoListVO> empSocInfoListVOList = empSocInfoDAO.selectEmpSocInfoStatistical(socStatisticalQuery, siteId);
        if(CollectionUtils.isEmpty(empSocInfoListVOList)){
            throw new HrException("暂无人员可导出");
        }
        List<EmpSocInfoStatisticalExportVO> empSocInfoStatisticalExportVOList = empSocInfoListVOList.stream()
                .sorted(Comparator.comparing(EmpSocInfoListVO::getMonth))
                .map(empSocInfoListVO -> empSocInfoListVO.to(EmpSocInfoStatisticalExportVO.class))
                .collect(Collectors.toList());
        for (EmpSocInfoStatisticalExportVO empSocInfoStatisticalExportVO : empSocInfoStatisticalExportVOList) {
            empSocInfoStatisticalExportVO.
                    setSocMonth(empSocInfoStatisticalExportVO.getYear()+"年"+empSocInfoStatisticalExportVO.getMonth()+"月");
        }
        return empSocInfoStatisticalExportVOList;
    }
}
